import zerorpc
import logging
import os
import errno 
import time
import inspect

from daemon import Daemon
from utils import Exp, _dmsg, _dwarn, _derror, \
    etchosts_update, cpu_usage, _exec_shell, _exec_pipe, \
    mutil_exec, _get_value, _human_unreadable, _human_readable, \
    _exec_pipe1, _lock_file, _exec_system

def __line__ ():
    caller = inspect.stack()[2]
    return int (caller[2])

class ZeroRpcSrvImpl(object):
    def __init__(self, config, foreground):
        self.foreground = foreground
        self.config = config
        self.shm = '/dev/shm/lich4/remotecopy/server'
        self.prefix = '::remotecopy-snapshot::'

    def _dinfo(self, msg):
        if self.foreground:
            print "%s/%d %s %s:%s INFO: %s" %(time.strftime('%Y-%m-%d %H:%M:%S'),
                    time.time(), os.getpid(), __file__, __line__(), msg)
        else:
            logging.info(msg)

    def _derror(self, msg):
        if self.foreground:
            print "%s/%d %s %s:%s ERROR: %s" %(time.strftime('%Y-%m-%d %H:%M:%S'),
                    time.time(), os.getpid(), __file__, __line__(), msg)
        else:
            logging.error(msg)

    def __get_volume_path(self, uuid, name, volume):
        #return "/::remotecopy-volume::/%s/%s:%s" % (uuid, name, volume[1:].replace('/', '-'))
        return volume

    def __volume_exists(self, volume):
        try:
            (out_msg, err_msg) = _exec_pipe1(["lich.inspect", "--stat", volume], 0, True, 5)
            return True
        except:
            return False

    def __volume_create(self, volume):
        self._dinfo("create volume :%s" %(volume))
        try:
            (out_msg, err_msg) = _exec_pipe1(["lichfs", "--mkdir", os.path.dirname(volume), "-p"], 0, True, 5)
        except Exp, e:
            if e.errno == errno.EEXIST:
                pass
            else:
                raise
        try:
            (out_msg, err_msg) = _exec_pipe1(["lichfs", "--touch", volume], 0, True, 5)
        except Exp, e:
            if e.errno == errno.EEXIST:
                pass
            else:
                raise

        self.__volume_exists(volume)

    def __volume_chunk_write(self, uuid, name, vol, last, i, context):
        chunk = ":%s/%s/%s/%s:%s" % (self.shm, uuid, name, vol[1:].replace('/', '-'), i)
        dir = os.path.dirname(chunk[1:])
        if not os.path.exists(dir):
            os.makedirs(dir)

        fd = open(chunk[1:], 'wb')
        fd.write(context)
        fd.close()

        vol_name = self.__get_volume_path(uuid, name, vol)
        print vol_name
        if not self.__volume_exists(vol_name):
            raise Exp(errno.EINVAL, "server volume %s not exists" %(vol_name))
        (out_msg, err_msg) = _exec_pipe1(["lichfs", "--copy", chunk, vol_name, str(i)], 0, True, 5)

    def __file_path(self, uuid, name, vol):
        path_name = "%s/remotecopy/server/%s/%s:%s" % (self.config.home, uuid, name, vol[1:].replace('/', '-'))
        if not os.path.exists(path_name):
            os.makedirs(path_name)

        return path_name

    def __file_write(self, uuid, name, vol, file, context):
        path_name = self.__file_path(uuid, name, vol)
        fd = open("%s/%s" % (path_name, file), 'wb')
        fd.write(str(context))
        fd.close()

    def __file_read(self, uuid, name, vol, file):
        try:
            path_name = self.__file_path(uuid, name, vol)
            fd = open("%s/%s" % (path_name, file), 'rb')
            context = fd.read()
            fd.close()

            return context
        except:
            return None

    def sync_all_check(self, uuid, name, vol, list):
        vol_name = self.__get_volume_path(uuid, name, vol)
        if not self.__volume_exists(vol_name):
            return True

        luuid = self.__file_read(uuid, name, vol, 'uuid')
        if uuid != luuid:
            raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        snap = self.__file_read(uuid, name, vol, 'last')
        print snap, "  ", list
        if not snap:
            return True
        elif len(list) == 1:
            if  snap != list[-1]:
                return True
        elif snap != list[-2] and snap != list[-1]:
            return True

        return False

    def sync_all_prep(self, uuid, name, vol, last):
        vol_name = self.__get_volume_path(uuid, name, vol)
        if not self.__volume_exists(vol_name):
            self.__volume_create(vol_name)
            self.__file_write(uuid, name, vol, 'uuid', uuid)
        else:
            luuid = self.__file_read(uuid, name, vol, 'uuid')
            if uuid != luuid:
                raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        self._dinfo("sync all prep last %s" % last)

        self.__file_write(uuid, name, vol, 'type', 'sync_all')
        self.__file_write(uuid, name, vol, 'status', 'prep')
        self.__file_write(uuid, name, vol, 'last', last)

    def sync_all_finish(self, uuid, name, vol, last, chknum):
        luuid = self.__file_read(uuid, name, vol, 'uuid')
        if uuid != luuid:
            raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        snap = self.__file_read(uuid, name, vol, 'last')
        if snap != last:
            raise Exp(errno.EINVAL, "server last snap %s not eq remote snap %s@%s" %(snap, vol, last))
        status = self.__file_read(uuid, name, vol, 'status')
        idx = self.__file_read(uuid, name, vol, 'idx')
        if status != 'end' or int(idx) != (chknum - 1):
            raise Exp(errno.EINVAL, "server last status '%s %s' can not set to finish" %(status, idx))
        self._dinfo("sync all finish /%s/%s/%s last %s chknum:%s" %(uuid, name, vol, last, chknum))
        self.__file_write(uuid, name, vol, 'status', 'finish')

    def __snapshot_create(self, uuid, name, vol):
        vol_name = self.__get_volume_path(uuid, name, vol)
        snap_name = "%s%d" %(self.prefix, time.time())
        self._dinfo("create snapshot:%s@%s"%(vol_name, snap_name))
        (out_msg, err_msg) = _exec_pipe1(["lich.snapshot", "--create", "%s@%s" %(vol_name, snap_name)], 0, True, 5)
        return snap_name

    def __snapshot_get_list(self, uuid, name, vol):
        list = []
        vol_name = self.__get_volume_path(uuid, name, vol)
        (out_msg, err_msg) = _exec_pipe1(["lich.snapshot", "--list", vol_name], 0, False, 5)
        if out_msg:
            return [x for x in out_msg.split() if x.startswith("%s"%(self.prefix))]

        return []

    def __snapshot_cleanup(self, uuid, name, vol, keep):
        list = self.__snapshot_get_list(uuid, name, vol)
        if len(list) > keep:
            vol_name = self.__get_volume_path(uuid, name, vol)
            snap_name = list[0]
            self._dinfo("remove snapshot:%s@%s"%(vol_name, snap_name))
            (out_msg, err_msg) = _exec_pipe1(["lich.snapshot", "--remove", "%s@%s" %(vol_name, snap_name)], 0, True, 5)

    def sync_increment_prep(self, uuid, name, vol, list):
        vol_name = self.__get_volume_path(uuid, name, vol)
        if not self.__volume_exists(vol_name):
            raise Exp(errno.EINVAL, "server volume '%s' not exists" %(vol_name))

        luuid = self.__file_read(uuid, name, vol, 'uuid')
        if uuid != luuid:
            raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        if len(list) < 2:
            raise Exp(errno.EINVAL, "/%s/%s/%s snap list %s length less than 2" %(uuid, name, vol, list))

        snap = self.__file_read(uuid, name, vol, 'last')
        if snap != list[-2]:
            raise Exp(errno.EINVAL, "/%s/%s/%s snap list %s the last snap %s" %(uuid, name, vol, list, snap))

        status = self.__file_read(uuid, name, vol, 'status')
        if status != 'finish':
            raise Exp(errno.EINVAL, "/%s/%s/%s status %s not finish" %(uuid, name, vol, status))

        self.__snapshot_create(uuid, name, vol)

        last = list[-1]
        self._dinfo("sync all prep last %s" % last)

        self.__file_write(uuid, name, vol, 'type', 'sync_increment')
        self.__file_write(uuid, name, vol, 'status', 'prep')
        self.__file_write(uuid, name, vol, 'last', last)

    def sync_increment_finish(self, uuid, name, vol, last, keep):
        luuid = self.__file_read(uuid, name, vol, 'uuid')
        if uuid != luuid:
            raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        snap = self.__file_read(uuid, name, vol, 'last')
        if snap != last:
            raise Exp(errno.EINVAL, "server last snap %s not eq remote snap %s@%s" %(snap, vol, last))
        status = self.__file_read(uuid, name, vol, 'status')
        if status == 'start':
            raise Exp(errno.EINVAL, "server last status '%s' can not set to finish" %(status))
        self.__file_write(uuid, name, vol, 'status', 'finish')

        self._dinfo("sync increment finish /%s/%s/%s last %s" %(uuid, name, vol, last))
        self.__snapshot_cleanup(uuid, name, vol, keep)

    def __sync_chunk_start(self, uuid, name, vol, last, i):
        snap = self.__file_read(uuid, name, vol, 'last')
        if snap != last:
            raise Exp(errno.EINVAL, "server last snap %s not eq remote snap %s@%s" %(snap, vol, last))
        self.__file_write(uuid, name, vol, 'status', 'start')
        self.__file_write(uuid, name, vol, 'idx', str(i))

    def __sync_chunk_end(self, uuid, name, vol, i):
        status = self.__file_read(uuid, name, vol, 'status')
        idx = self.__file_read(uuid, name, vol, 'idx')
        if status == "start" and int(idx) == i:
            self.__file_write(uuid, name, vol, 'status', 'end')
        else:
            raise Exp(errno.EINVAL, "server status '%s' can not change to 'end %s'" %(status, i))

    def sync_chunk(self, uuid, name, vol, last, i, chunk):
        luuid = self.__file_read(uuid, name, vol, 'uuid')
        if uuid != luuid:
            raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        self.__sync_chunk_start(uuid, name, vol, last, i)
        self.__volume_chunk_write(uuid, name, vol, last, i, chunk)
        self.__sync_chunk_end(uuid, name, vol, i)

    def sync_break_check(self, uuid, name, vol, list):
        luuid = self.__file_read(uuid, name, vol, 'uuid')
        if uuid != luuid:
            raise Exp(errno.EINVAL, "volume %s is not owner by %s" %(vol, uuid))

        status = self.__file_read(uuid, name, vol, 'status')
        self._dinfo("sync break check status:%s" % status)
        if status != 'finish':
            return True
        last = self.__file_read(uuid, name, vol, 'last')
        self._dinfo("sync break check last %s list:%s" % (last, list))
        if last != list[-1]:
            return True

    def get_snapshot_last(self, uuid, name, vol):
        last = self.__file_read(uuid, name, vol, 'last')
        return last

class ZeroRpcSrv(Daemon):
    def __init__(self, config):
        self.config = config
        self.path = os.path.join(self.config.home, 'remotecopy')
        if not os.path.exists(self.path):
            os.mkdir(self.path)
        pidfile = '/var/run/fusionstack_zerorpc_server.lock'
        super(ZeroRpcSrv, self).__init__(pidfile, name='zerorpc_srv')

    def listen(self, port, foreground):
        logging.basicConfig(level=logging.DEBUG,
                format = '%(asctime)s/%(created)d %(process)d/%(threadName)s %(filename)s:%(lineno)d %(levelname)s %(message)s',
                datefmt = '%Y-%m-%d %H:%M:%S',
                filename = os.path.join(self.config.home, 'log/remotecopy_server.log'),
                filemode = 'a')

        self.foreground = foreground
        self.port = port
        if (foreground):
            self.run()
        else:
            self.start()

    def run(self):
        s = zerorpc.Server(ZeroRpcSrvImpl(self.config, self.foreground))
        s.bind("tcp://0.0.0.0:%d" % self.port)
        if (self.foreground):
            print("========sevice start========")
        else:
            self._dinfo("========sevice start========")
        s.run()
